using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net;
using System.Net.Sockets;
using System.Security;
using System.Text;
using System.Threading;
using Universe.Farm.Service.Mesages;

namespace Universe.Farm.Service
{
    class NetListenerAgent: FarmAgent
    {
        private Thread _Thread = null;
        private ManualResetEvent _StopEvent = null;

        public NetListenerAgent(FarmState farmState)
            : base(farmState)
        {
        }

        public void Start()
        {
            _StopEvent = new ManualResetEvent(false);
            _Thread = new Thread(ThreadStart);
            _Thread.Name = "Server Farm Network Listener";
            _Thread.IsBackground = true;
            _Thread.Start();
        }


        public void Stop()
        {
            if (_Thread != null && _StopEvent != null)
            {
                if (_listener != null)
                {
                    _listener.Close();
                    _listener = null;
                }

                _StopEvent.Set();
                if (!_Thread.Join(1000))
                    _Thread.Abort();

                _Thread = null;
                _StopEvent.Close(); 
                _StopEvent = null;
            }
        }

        private UdpClient _listener;

        private void ThreadStart()
        {
            _listener = new UdpClient(TheFarmState.CommunicationParameters.Port);
            _listener.EnableBroadcast = true;
            while (true)
            {
                IPEndPoint ep = new IPEndPoint(IPAddress.Any, 0);
                byte[] bytes = null;
                try
                {
                    if (_listener == null)
                        return;

                    bytes = _listener.Receive(ref ep);
                }
                catch(SocketException se)
                {
                    bool isListenerClosed = se.ErrorCode == 10004;
                    if (isListenerClosed)
                        return;

                    Trace.WriteLine("LISTENER EXCEPTION: " + se);
                    throw;
                }

                IBufferProtocol bp = TheFarmState.Config.ResolveRequired<IBufferProtocol>();
                try
                {
                    bytes = bp.Decode(bytes);
                }
                catch(VerificationException)
                {
                    Trace.WriteLine("Wrong Packed Arrived");
                    continue;
                }

                IMessage msg = MessageIO.Parse(bytes);

                AnnounceMessage ann = msg as AnnounceMessage;
                if (ann != null)
                    HandleAnnounce(ann);

                ByeMessage bye = msg as ByeMessage;
                if (bye != null)
                    HadleBye(bye);

                LookupRequestMessage look = msg as LookupRequestMessage;
                if (look != null)
                    HandleLookup(look);

                SetVisibilityMessage setVisibility = msg as SetVisibilityMessage;
                if (setVisibility != null)
                    HandleSetVisibility(setVisibility);
            }
        }

        private void HandleSetVisibility(SetVisibilityMessage visibility)
        {
            bool isNew = AcquireStation.IsNew(TheFarmState, visibility.MessageHeader);
            if (isNew)
            {
                foreach (DeliveryAgent agent in TheFarmState.DeliveryStations)
                {
                    agent.Deliver(visibility);
                }

                throw new NotImplementedException();
            }
        }

        private void HandleLookup(LookupRequestMessage look)
        {
            bool isNew = AcquireStation.IsNew(TheFarmState, look.MessageHeader);
            Trace.WriteLine(DateTime.Now.ToString("HH:mm:ss ") + "Lookup IS ARRIVED " + (isNew ? "(new)" : "(copy)") + " from " + look.MessageHeader.ComputerID + ": " + look.MessageHeader.Stamp);
            if (isNew)
            {
                foreach (DeliveryAgent agent in TheFarmState.DeliveryStations)
                {
                    agent.Deliver(look);
                }

                foreach (ServiceListenerAgent agent in TheFarmState.ServiceListeners)
                {
                    bool isIt = false;
                    if (look.Scope.IsFullScope)
                        isIt = true;

                    else
                    {
                        foreach (ServicesScope.Item item in look.Scope.Items)
                        {
                            if ((item.IsAnyComputer || (item.Computer == CommunicationParameters.ComputerID))
                                && (item.IsAnyService || item.ServiceName == agent.ServiceName))
                            {
                                isIt = true;
                            }
                        }
                    }

                    if (isIt)
                        agent.PingAndAnnounce();
                }
            }
        }

        private void HadleBye(ByeMessage bye)
        {
            bool isNew = AcquireStation.IsNew(TheFarmState, bye.MessageHeader);
            Trace.WriteLine(DateTime.Now.ToString("HH:mm:ss ") + "BYE IS ARRIVED " + (isNew ? "(new)" : "(copy)") + " from " + bye.MessageHeader.ComputerID + ": " + bye.MessageHeader.Stamp);
            if (isNew)
                foreach (DeliveryAgent agent in TheFarmState.DeliveryStations)
                    agent.Deliver(bye);

            AcquireStation.Acquire(TheFarmState, bye);
        }

        private void HandleAnnounce(AnnounceMessage ann)
        {
            bool isNew = AcquireStation.Aquiare(TheFarmState, ann);

            Debug.WriteLine(
                DateTime.Now.ToString("HH:mm:ss ") 
                + "Message " + (isNew ? "(new)" : "(copy)") 
                + " #" + ann.Stamp 
                + " arrived from " + ann.Computer 
                + ". Service " + ann.IdService + " is " + (ann.IsOK ? "OK" : "DONE"));

            if (isNew)
            {
                foreach (DeliveryAgent agent in TheFarmState.DeliveryStations)
                {
                    agent.Deliver(ann);
                }
            }
        }
    }
}
